/**
 * 
 */
package adnotatio.common.io;

import com.google.gwt.core.client.GWT;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.RequestBuilder.Method;
import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * This is a common interface for resource loaded. This class tries to load the
 * resource specified by the local path with the given content and calls the
 * given callback object when the resource is loaded or the load process is
 * failed. This class takes the local path to the resource to load and build the
 * full URL using the {@link GWT#getModuleBaseURL()} method:
 * <code>String URL = GWT.getModuleBaseURL() + path</code>
 * 
 * @author kotelnikov
 */
public class ResourceLoader {

    /**
     * If this flag is <code>true</code> then this loader uses the POST HTTP
     * method to send the information; otherwise the GET HTTP method is used.
     */
    private boolean fPost;

    /**
     * The full URL to the local resource to load.
     */
    private String fUrl;

    /**
     * The default constructor initializing the full URL to the resource to
     * load.
     * 
     * @param path the local path to the resource to load
     * @param post if this flag is <code>true</code> then this loader uses the
     *        POST method to send the information; otherwise the GET method is
     *        used
     */
    public ResourceLoader(String path) {
        this(path, true);
    }

    /**
     * The default constructor initializing the full URL to the resource to
     * load.
     * 
     * @param path the local path to the resource to load
     * @param post if this flag is <code>true</code> then this loader uses the
     *        POST method to send the information; otherwise the GET method is
     *        used
     */
    public ResourceLoader(String path, boolean post) {
        fUrl = GWT.getModuleBaseURL() + path;
        fPost = post;
    }

    /**
     * Returns the method of the HTTP call
     * 
     * @return the method of the HTTP call
     */
    protected RequestBuilder.Method getMethod() {
        return fPost ? RequestBuilder.POST : RequestBuilder.GET;
    }

    /**
     * Loads the local resource and calls the given callback object when the
     * resource is loaded or loading process is failed. This method uses the
     * POST HTTP method to submit the request.
     * 
     * @param callback the callback method used to notify about results of the
     *        loading process
     */
    public final void load(AsyncCallback callback) {
        load("", callback);
    }

    /**
     * Loads the local resource and calls the given callback object when the
     * resource is loaded or loading process is failed. This method uses the
     * POST HTTP method to submit the given content and to retrieve the
     * response.
     * 
     * @param content the content to submit to the server
     * @param callback the callback method used to notify about results of the
     *        loading process
     */
    public final void load(String content, final AsyncCallback callback) {
        Method method = getMethod();
        RequestBuilder request = new RequestBuilder(method, fUrl);
        try {
            request.sendRequest(content, new RequestCallback() {

                /**
                 * @see com.google.gwt.http.client.RequestCallback#onError(com.google.gwt.http.client.Request,
                 *      java.lang.Throwable)
                 */
                public void onError(Request request, Throwable exception) {
                    callback.onFailure(exception);
                }

                /**
                 * @see com.google.gwt.http.client.RequestCallback#onResponseReceived(com.google.gwt.http.client.Request,
                 *      com.google.gwt.http.client.Response)
                 */
                public void onResponseReceived(
                    Request request,
                    Response response) {
                    try {
                        ResourceLoader.this.onSuccess(
                            request,
                            response,
                            callback);
                    } catch (ResourceLoaderException e) {
                        callback.onFailure(e);
                    } catch (Exception e) {
                        callback.onFailure(new ResourceLoaderException(
                            request,
                            response,
                            e));
                    }
                }

            });
        } catch (RequestException e) {
            callback.onFailure(e);
        }

    }

    /**
     * This method is used to notify the given callback object about results of
     * the loading process. This method can be overloaded in subclasses to
     * re-define the results which should be send to the callback object. This
     * method is called only if the server successfully returned a result.
     * 
     * @param request the initial HTTP request object
     * @param response the response from the server
     * @param callback the callback object to notify
     * @throws Exception
     */
    protected void onSuccess(
        Request request,
        Response response,
        AsyncCallback callback) throws Exception {
        callback.onSuccess(response);
    }

}